/* eslint-disable no-param-reassign */
/* eslint-disable no-mixed-operators */
/* eslint-disable object-curly-newline */
/* eslint-disable no-plusplus */
import Base from 'widget-base';
import find from 'lodash.find';
import sortBy from 'lodash.sortby';

/**
 * 将点 p 绕点 c 旋转 theta 度。
 * @param {Array} p 一个包含点 x 和 y 坐标的数组 [x, y]。
 * @param {Array} c 一个包含中心点 x 和 y 坐标的数组 [x, y]。
 * @param {Number} theta 旋转角度（度数）。
 * @returns {Array} 旋转后点的新坐标 [x', y']。
 */
function rotatePoint(p, c, theta) {
  // 解构点 p 的 x 和 y 坐标
  const [px, py] = p;
  // 如果旋转角度为 0，则直接返回原始坐标
  if (!theta) return [px, py];
  
  // 解构中心点 c 的 x 和 y 坐标
  const [cx, cy] = c;
  // 计算点 p 到中心点 c 的距离
  const rx = px - cx;
  const ry = py - cy;
  // 将角度转换为弧度
  const rad = theta * Math.PI / 180;
  // 计算旋转矩阵的余弦和正弦值
  const cos = Math.cos(rad);
  const sin = Math.sin(rad);
  // 应用旋转矩阵进行旋转
  const nx = rx * cos - ry * sin;
  const ny = rx * sin + ry * cos;
  // 将旋转后的点重新相对于中心点 c 平移
  return [nx + cx, ny + cy];
}

const INF = 10000;

/**
 * 给定p, q, r三点共线，判断q点是否在线段pr上
 *
 * 参数
 * p: [x1, y1]
 * q: [x2, y2]
 * r: [x3, y3]
 */
function onSegment(p, q, r) {
  const [x1, y1] = p;
  const [x2, y2] = q;
  const [x3, y3] = r;
  if (
    x2 <= Math.max(x1, x3) &&
    x2 >= Math.min(x1, x3) &&
    y2 <= Math.max(y1, y3) &&
    y2 >= Math.min(y1, y3)
  ) {
    return true;
  }
  return false;
}

/**
 * 判断 p, q, r三点的位置关系
 *
 * 参数
 * p: [x1, y1]
 * q: [x2, y2]
 * r: [x3, y3]
 *
 * 返回值
 * 0: 共线
 * 1: p, q, r 顺时针
 * 2: p, q, r 逆时针
 */
function orientation(p, q, r) {
  const [x1, y1] = p;
  const [x2, y2] = q;
  const [x3, y3] = r;
  const val = (y2 - y1) * (x3 - x2) - (x2 - x1) * (y3 - y2);
  if (val === 0) return 0; // 共线
  return val > 0 ? 1 : 2; // 顺时针或逆时针
}

/**
 * 判断线段p1q1和线段p2q2是否有交点
 * p1: [x1, y1]
 * q1: [x2, y2]
 * p2: [x3, y3]
 * q2: [x4, y4]
 */
function doIntersect(p1, q1, p2, q2) {
  const o1 = orientation(p1, q1, p2);
  const o2 = orientation(p1, q1, q2);
  const o3 = orientation(p2, q2, p1);
  const o4 = orientation(p2, q2, q1);

  // General case
  if (o1 !== o2 && o3 !== o4) return true;

  // Special Cases
  // p1, q1 and p2 are colinear and p2 lies on segment p1q1
  if (o1 === 0 && onSegment(p1, p2, q1)) return true;

  // p1, q1 and p2 are colinear and q2 lies on segment p1q1
  if (o2 === 0 && onSegment(p1, q2, q1)) return true;

  // p2, q2 and p1 are colinear and p1 lies on segment p2q2
  if (o3 === 0 && onSegment(p2, p1, q2)) return true;

  // p2, q2 and q1 are colinear and q1 lies on segment p2q2
  if (o4 === 0 && onSegment(p2, q1, q2)) return true;

  return false; // Doesn't fall in any of the above cases
}

function calcDistance(p1, p2) {
  const v = [p1[0] - p2[0], p1[1] - p2[1]];
  return Math.sqrt(v[0] * v[0] + v[1] * v[1]);
}

// function calcOuterProduct(v1, v2) {
//   return v1[0] * v2[1] - v1[1] * v2[0];
// }

// function getVectorFromTwoPoints(p1, p2) {
//   return [p2[0] - p1[0], p2[1] - p1[1]];
// }

// function isConvexPolygon(points) {
//   if (points.length < 3) return false;
//   let prevPoint = points[points.length - 1];
//   let currPoint = points[0];
//   let nextPoint = points[1];
//   let v1 = getVectorFromTwoPoints(currPoint, prevPoint);
//   let v2 = getVectorFromTwoPoints(currPoint, nextPoint);
//   const outerProduct0 = calcOuterProduct(v1, v2);

//   for (let i = 1; i < points.length; i++) {
//     prevPoint = points[i - 1];
//     currPoint = points[i];
//     nextPoint = points[(i + 1) % points.length];
//     v1 = getVectorFromTwoPoints(currPoint, prevPoint);
//     v2 = getVectorFromTwoPoints(currPoint, nextPoint);
//     const outerProduct = calcOuterProduct(v1, v2);
//     if (outerProduct * outerProduct0 < 0) return false;
//   }

//   return true;
// }

function calcLineEquation(linePoints) {
  // equation: ax + by + c = 0
  const x1 = linePoints[0][0];
  const y1 = linePoints[0][1];
  const x2 = linePoints[1][0];
  const y2 = linePoints[1][1];
  return {
    a: y2 - y1,
    b: x1 - x2,
    c: (x2 * y1) - (x1 * y2),
  };
}

function getIntersectionOfTwoLines(line1, line2) {
  const { a: a1, b: b1, c: c1 } = calcLineEquation(line1);
  const { a: a2, b: b2, c: c2 } = calcLineEquation(line2);
  const determinant = (a1 * b2) - (a2 * b1);
  if (determinant === 0) return null;
  return [((b1 * c2) - (b2 * c1)) / determinant, ((c1 * a2) - (c2 * a1)) / determinant];
}

function getIntersectionOfTwoSegments(seg1, seg2) {
  const [p1, p2] = seg1;
  const [q1, q2] = seg2;
  if (doIntersect(p1, p2, q1, q2)) {
    return getIntersectionOfTwoLines(seg1, seg2);
  }
  return null;
}

/**
 * p: [x, y]
 * polygon: [[x1, y1], [x2, y2], ...]
 * Returns true if the point p lies inside the polygon[] with n vertices
 */
function isPointInsidePolygon(p, polygon) {
  const n = polygon.length;
  // 至少三个顶点
  if (n < 3) return false;

  // 模拟一个水平的从点p出发的射线
  const extreme = [INF, p[1]];

  // 计算射线和多边形的边的交点的个数
  const intersections = [];
  let i = 0;
  do {
    const next = (i + 1) % n;

    // Check if the line segment from 'p' to 'extreme' intersects
    // with the line segment from 'polygon[i]' to 'polygon[next]'
    if (doIntersect(polygon[i], polygon[next], p, extreme)) {
      // If the point 'p' is colinear with line segment 'i-next',
      // then check if it lies on segment. If it lies, return true,
      // otherwise false
      if (orientation(polygon[i], p, polygon[next]) === 0) return onSegment(polygon[i], p, polygon[next]);
      const intersection = getIntersectionOfTwoLines([polygon[i], polygon[next]], [p, extreme]);
      intersections.push(intersection);
    }
    i = next;
  } while (i !== 0);

  // 去掉重复点
  const cleanedIntersections = [];
  intersections.forEach((point) => {
    for (let j = 0; j < cleanedIntersections.length; j++) {
      const d = calcDistance(point, cleanedIntersections[j]);
      if (d < 0.1) return;
    }
    cleanedIntersections.push(point);
  });

  // Return true if count is odd, false otherwise
  return cleanedIntersections.length % 2 === 1;
}


export default Base.Model.extend({
  defaults: {
    widgetName: 'cutShape',
    fullPath: '',
    widgetWidth: 800,
    widgetHeight: 600,
    gridWidth: 40,
    gridHeight: 40,
    presetShape: null,
    isDrawCircle:false,
    isShowCircle:false,
    severalCopies:1,

    // runtime data
    initPreset: {},
    foldsNumber: 1, // 折叠次数
    splitLines: [], //用于存储分割线的集合
    isSplitted: false, //用于表示当前是否已进行分割
    shapes: [], //用于存储形状的集合
    operations: [], // 用于存储操作的集合
    presetShapeMiddlePoints: null, //用于存储预设形状的中点集合
    allShapes: [], //用于存储裁切的所有形状的集合
  },

  initialize() {
    const fullPath = this.get('fullPath');
    this.set('fullPath', fullPath.replace(/\/+$/, ''));
    this.reset();
  },

  reset() {
    this.set('splitLines', []);
    this.set('isSplitted', false);
    this.set('shapes', []);
    this.set('operations', []);
  },

  addSplitLine(pos) {
    const widgetWidth = this.get('widgetWidth');
    const widgetHeight = this.get('widgetHeight');
    const minSide = Math.min(widgetWidth, widgetHeight);
    const lineSize = minSide > 200 ? 100 : minSide / 2;
    const p1 = [pos[0] - lineSize, pos[1] - lineSize];
    const p2 = [pos[0] + lineSize, pos[1] + lineSize];
    if (p1[0] < 0) {
      p1[0] = 0;
      p1[1] -= p1[0];
    }
    if (p1[1] < 0) {
      p1[1] = 0;
      p1[0] -= p1[1];
    }
    if (p2[0] > widgetWidth) {
      p2[0] = widgetWidth;
      p2[1] -= p2[0] - widgetWidth;
    }
    if (p2[1] > widgetHeight) {
      p2[1] = widgetHeight;
      p2[0] -= p2[1] - widgetHeight;
    }
    // const middlePoints = [];
    // const presetShape = this.get('presetShape');
    // for (let i = 0; i < presetShape.points.length; i++) {
    //   const p = presetShape.points[i];
    //   const pNext = presetShape.points[(i + 1) % presetShape.points.length];
    //   middlePoints.push([(p[0] + pNext[0]) / 2, (p[1] + pNext[1]) / 2]);
    // }
    // const p1 = middlePoints[0];
    // const p2 = middlePoints[2];
    const splitLines = this.get('splitLines');
    const line = { points: [p1, p2] };
    splitLines.push(line);
    this.get('operations').push({ type: 'addSplitLine', data: JSON.parse(JSON.stringify(line)) });
  },

  findLinePointAtPos(pos) {
    const tolerance = 20;
    const splitLines = this.get('splitLines');
    for (let i = splitLines.length - 1; i >= 0; i--) {
      for (let j = splitLines[i].points.length - 1; j >= 0; j--) {
        const p = splitLines[i].points[j];
        if (Math.abs(p[0] - pos[0]) < tolerance && Math.abs(p[1] - pos[1]) < tolerance) {
          return { lineIndex: i, pointIndex: j };
        }
      }
    }
    return null;
  },

  splitShape(polygonPoints, segmentPoints) {
    const allIntersections = [];
    for (let i = 0; i < polygonPoints.length; i++) {
      const edge = [polygonPoints[i], polygonPoints[(i + 1) % polygonPoints.length]];
      const intersection = getIntersectionOfTwoSegments(edge, segmentPoints);
      if (intersection) {
        allIntersections.push({ x: intersection[0], y: intersection[1], edge: i });
      }
    }

    const sortKey = (segmentPoints[0][0] === segmentPoints[1][0] ? 'y' : 'x');
    const sortedIntersections = sortBy(allIntersections, [sortKey]);

    // 去掉单独的点，并考虑非凸多边形的情形
    const omittedIntersectionIndexes = [];
    let intersectionIndex = 0;
    while (intersectionIndex < sortedIntersections.length) {
      const currentIntersection = sortedIntersections[intersectionIndex];
      const nextIntersection = sortedIntersections[++intersectionIndex];
      if (!nextIntersection) {
        omittedIntersectionIndexes.push(intersectionIndex - 1);
      } else {
        const center = [
          (currentIntersection.x + nextIntersection.x) / 2,
          (currentIntersection.y + nextIntersection.y) / 2,
        ];
        if (!isPointInsidePolygon(center, polygonPoints)) {
          omittedIntersectionIndexes.push(intersectionIndex - 1);
        } else {
          intersectionIndex += 1;
        }
      }
    }
    for (let i = omittedIntersectionIndexes.length - 1; i >= 0; i--) {
      sortedIntersections.splice(omittedIntersectionIndexes[i], 1);
    }

    const polygons = [];
    let polygon = { points: [] };
    polygons.push(polygon);
    for (let i = 0; i < polygonPoints.length; i++) {
      polygon.points.push({ x: polygonPoints[i][0], y: polygonPoints[i][1] });
      const intersection = find(sortedIntersections, { edge: i });
      if (intersection) {
        polygon.points.push(intersection);
        const index = sortedIntersections.indexOf(intersection);
        polygon.crossback = (index % 2 === 0 ? index + 1 : index - 1);
        const crossbackPolygon = find(polygons, { crossback: index });
        if (crossbackPolygon) {
          polygon = crossbackPolygon;
        } else {
          polygon = { points: [] };
          polygons.push(polygon);
        }
        polygon.points.push(intersection);
      }
    }
    return polygons.map(poly => ({ points: poly.points.map(p => [p.x, p.y]) }));
  },

  findShapeAtPos(pos) {
    const shapes = this.get('shapes');
    for (let i = shapes.length - 1; i >= 0; i--) {
      const isInside = isPointInsidePolygon(pos, shapes[i].transformedPoints || shapes[i].points);
      if (isInside) return shapes[i];
    }
    return null;
  },

  getShapeBoundingBox(shape) {
    const points = shape.transformedPoints || shape.points;
    const xCoords = points.map(p => p[0]);
    const yCoords = points.map(p => p[1]);
    const xMin = Math.min(...xCoords);
    const xMax = Math.max(...xCoords);
    const yMin = Math.min(...yCoords);
    const yMax = Math.max(...yCoords);
    return { x: xMin, y: yMin, w: xMax - xMin, h: yMax - yMin };
  },

  getShapeBox(shape) {
    const { points } = shape;
    const xCoords = points.map(p => p[0]);
    const yCoords = points.map(p => p[1]);
    const xMin = Math.min(...xCoords);
    const xMax = Math.max(...xCoords);
    const yMin = Math.min(...yCoords);
    const yMax = Math.max(...yCoords);
    return { x: xMin, y: yMin, w: xMax - xMin, h: yMax - yMin };
  },

  calcDistance(p1, p2) {
    return calcDistance(p1, p2);
  },

  rotatePoint(p, center, degree) {
    return rotatePoint(p, center, degree);
  },
  updateShapeTransformedPoints(shape) {
    if (!shape.rotation) {
      shape.transformedPoints = null;
    } else {
      const shapeBox = this.getShapeBox(shape);
      const center = [shapeBox.x + shapeBox.w / 2, shapeBox.y + shapeBox.h / 2];
      shape.transformedPoints = shape.points.map(p => rotatePoint(p, center, shape.rotation));
      shape.transformedSymmetricPoints = shape.symmetricPoints.map(p => rotatePoint(p, center, shape.rotation));
    }
  },
});
